package 结构性模式


import java.sql.*
import java.util.*
import java.util.concurrent.ArrayBlockingQueue
import java.util.function.Supplier
import javax.sql.DataSource


/**
 * 为其他对象提供一种代理以控制对这个对象的访问
 * 1. 远程代理: 代理将对接口的方法转为远程调用
 * 2. 虚代理: 调用者持有一个代理对象，但真正的对象尚未创建
 * 3. 保护代理: 控制对原始对象的访问，常用于鉴权
 * 4. 智能引用: 通过内部的计数器可以在外部调用者都不使用后自动释放它
 */


abstract class AbstractConnectionProxy : Connection {
    protected abstract fun getRealConnection(): Connection

    override fun createStatement(): Statement {
        return getRealConnection().createStatement()
    }

    override fun prepareStatement(sql: String): PreparedStatement? {
        return getRealConnection().prepareStatement(sql)
    }
}


abstract class LazyConnectionProxy(private val supplier: Supplier<Connection>) : AbstractConnectionProxy() {
    private var target: Connection? = null

    override fun close() {
        if (target != null) {
            println("Close connection: $target")
            //super.close()
        }
    }

    override fun getRealConnection(): Connection {
        if (target == null) {
            target = supplier.get()
        }
        return target as Connection
    }
}

abstract class LazyDataSource(private val url: String, private val username: String, private val password: String) :
    DataSource {
    override fun getConnection(): Connection? {
        /*
        LazyConnectionProxy {
            try {
                val conn = DriverManager.getConnection(url, username, password)
                println("Open connection: $conn")
                return@LazyConnectionProxy conn
            } catch (e: SQLException) {
                throw RuntimeException(e)
            }
        }*/
        return null
    }
}

abstract class PooledConnectionProxy(private val idleQueue: Queue<PooledConnectionProxy>, val target: Connection) :
    AbstractConnectionProxy() {
    override fun close() {
        println("Fake close and released to idle queue for future reuse: $target")
        idleQueue.offer(this)
    }

    override fun getRealConnection(): Connection {
        return target
    }
}

abstract class PooledDataSource(private val url: String, private val username: String, private val password: String) :
    DataSource {
    private val idleQueue = ArrayBlockingQueue<PooledConnectionProxy>(100)
    override fun getConnection(username: String, password: String): Connection {
        var conn = idleQueue.poll()
        if (conn == null) {
            conn = openNewConnection()
        } else {
            println("Return pooled connection: " + conn.target)
        }
        return conn
    }

    private fun openNewConnection(): PooledConnectionProxy? {
        val conn = DriverManager.getConnection(url, username, password)
        println("Open new connection: $conn")
        //return PooledConnectionProxy(idleQueue, conn)
        return null
    }

}

fun main4() {
    /*
    val lazyDataSource: DataSource = LazyDataSource("", "", "")
    println("get lazy connection...")
    lazyDataSource.connection.use { }
    println("get lazy connection...")
    lazyDataSource.connection.use { conn2 ->
        conn2.prepareStatement("SELECT * FROM students").use { ps ->
            ps.executeQuery().use { rs ->
                while (rs.next()) {
                    println(rs.getString("name"))
                }
            }
        }
    }
     */
}


/**
 * Decorator模式让调用者自己创建核心类，然后组合各种功能，
 * 而Proxy模式决不能让调用者自己创建再组合，否则就失去了代理的功能。
 * Proxy模式让调用者认为获取到的是核心类接口，但实际上是代理类。
 */


// 抽象主题
interface Image {
    fun display()
}

// 真实主题
class RealImage(private val filename: String) : Image {
    init {
        loadFromDisk()
    }

    private fun loadFromDisk() {
        println("从磁盘加载图片: $filename")
    }

    override fun display() {
        println("显示图片: $filename")
    }
}

// 代理
class ProxyImage(private val filename: String) : Image {
    private var realImage: RealImage? = null

    override fun display() {
        if (realImage == null) {
            realImage = RealImage(filename)
        }
        realImage?.display()
    }
}

// 客户端代码
fun main() {
    val image: Image = ProxyImage("image.jpg")

    // 第一次调用会创建真实对象并加载图片
    image.display()

    // 第二次调用只会直接显示图片，不会再次加载
    image.display()
}